package com.zx._03_并发.chapter_07_limit;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @version v1.0
 * @Project: knowledge
 * @Title: LimitTest
 * @Description: 计数器
 *         计数器是最简单粗暴的算法。比如某个服务最多只能每秒钟处理100个请求。我们可以设置一个1秒钟的滑动窗口，窗口中有10个格子，
 *         每个格子100毫秒，每100毫秒移动一次，每次移动都需要记录当前服务请求的次数。 内存中需要保存10次的次数。
 *         可以用数据结构LinkedList来实现。格子每次移动的时候判断一次，当前访问次数和LinkedList中最后一个相差是否超过100，如果超过就需要限流了。
 * @author: zhangxue
 * @date: 2018年12月8日下午9:44:15
 * @company: 未知之地
 * @Copyright: Copyright (c) 2018
 */
public class LimitTest {

    private final static ExecutorService pool = Executors.newCachedThreadPool();


    private final static LimitTest test = new LimitTest();

    public static void main(String[] args) {
        //服务访问次数，可以放在Redis中，实现分布式系统的访问计数
        Long counter = 0L;
        //使用LinkedList来记录滑动窗口的10个格子。

        final LinkedBlockingQueue<Long> ll = new LinkedBlockingQueue<Long>();
        pool.execute(() -> {
            while (true) {
                try {
                    ll.put(counter);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }

                if (ll.size() > 2) {//确保只有5个
                    ll.poll();
                }

                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        for (int i = 0; i < 100; i++) {
            pool.execute(() -> {
                while (true) {
                    try {
                        if (ll.take() != null) {
                            test.get();
                            break;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    public void get() {
        System.out.println(Thread.currentThread().getName());
    }
}
